﻿using System;
using System.Linq;
using DependencyAnalysis.DataModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PrinciplesP = DependencyAnalysis.Principles;

namespace UnitTests.Principles
{
    [TestClass]
    public class DependOnStablePrincipleTest
    {
        private DependencyModel _dependencyModel;

        [TestInitialize]
        public void Initialize()
        {
            _dependencyModel = new DependencyModel();
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 2, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 3, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(2, 3, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(3, 4, true, true);
            _dependencyModel.Add(descriptor);
        }

        [TestMethod]
        public void WhenTypeDescriptorRaiseArgumentException()
        {
            Assert1.Throws<ArgumentException>(() => PrinciplesP.StableDependencies<TypeDescriptor>(_dependencyModel));
        }

        [TestMethod]
        public void WhenMemberDescriptorRaiseArgumentException()
        {
            Assert1.Throws<ArgumentException>(() => PrinciplesP.StableDependencies<MemberDescriptor>(_dependencyModel));
        }

        [TestMethod]
        public void WhenDependencyDescriptorRaiseArgumentException()
        {
            Assert1.Throws<ArgumentException>(() => PrinciplesP.StableDependencies<DependencyDescriptor>(_dependencyModel));
        }

        [TestMethod]
        public void ModulesWhenNoViolationsThenInstableDependencyCountIsZero()
        {
            var violations = PrinciplesP.StableDependencies<ModuleDescriptor>(_dependencyModel);
            Assert.AreEqual(0, violations.Count);
        }

        [TestMethod]
        public void ModulesWhenOneViolationThenInstableDependencyCountIsOne()
        {
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 6, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 7, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 8, true, true);
            _dependencyModel.Add(descriptor);

            var violations = PrinciplesP.StableDependencies<ModuleDescriptor>(_dependencyModel);
            Assert.AreEqual(1, violations.Count);
        }

        [TestMethod]
        public void ModulesWhenOneViolationThenTupleHasCorrectValues()
        {
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 6, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 7, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 8, true, true);
            _dependencyModel.Add(descriptor);

            var violation = PrinciplesP.StableDependencies<ModuleDescriptor>(_dependencyModel).First();

            descriptor = DependencyModelTest.BuildDependencyDescriptor(3, 4, true, true);
            Assert.AreEqual(descriptor.Source.ImplementingType.Submodule.Module, violation.Item1);
            Assert.AreEqual(descriptor.Target.ImplementingType.Submodule.Module, violation.Item3);
            Assert.AreEqual(1d/3d, violation.Item2); 
            Assert.AreEqual(0.8d, violation.Item4);
        }

        [TestMethod]
        public void SubmodulesWhenNoViolationsThenInstableDependencyCountIsZero()
        {
            var violations = PrinciplesP.StableDependencies<SubmoduleDescriptor>(_dependencyModel);
            Assert.AreEqual(0, violations.Count);
        }

        [TestMethod]
        public void SubmodulesWhenOneViolationThenInstableDependencyCountIsOne()
        {
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 6, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 7, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 8, true, true);
            _dependencyModel.Add(descriptor);

            var violations = PrinciplesP.StableDependencies<SubmoduleDescriptor>(_dependencyModel);
            Assert.AreEqual(1, violations.Count);
        }

        [TestMethod]
        public void SubmodulesWhenOneViolationThenTupleHasCorrectValues()
        {
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 6, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 7, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 8, true, true);
            _dependencyModel.Add(descriptor);

            var violation = PrinciplesP.StableDependencies<SubmoduleDescriptor>(_dependencyModel).First();

            descriptor = DependencyModelTest.BuildDependencyDescriptor(3, 4, true, true);
            Assert.AreEqual(descriptor.Source.ImplementingType.Submodule, violation.Item1);
            Assert.AreEqual(descriptor.Target.ImplementingType.Submodule, violation.Item3);
            Assert.AreEqual(1d / 3d, violation.Item2);
            Assert.AreEqual(0.8d, violation.Item4);
        }
    }
}
